<template>
  <g class="node" ref="node" @click="click" @mousedown="mousedown">
    <g>
      <rect :rx="opt.borderRadius" :ry="opt.borderRadius" :width="width" :height="opt.height" :fill="data.bgColor" :stroke="strokeColor" :stroke-width="strokeWidth"/>
      <text :x="opt.padding" :y="textY" :fill="data.color" ref="text" :style="{ fontSize: opt.fontSize + 'px' }">{{ data.text }}</text>
    </g>
    <yc-point v-if="data.hasHead" :option="opt.point" :category="1" ref="head" @update="updateHead"></yc-point>
    <yc-point v-if="data.hasTail" :option="opt.point" :category="2" ref="tail" @update="updateTail"></yc-point>
  </g>
</template>

<script>
import { TweenMax } from 'gsap'
import Draggable from 'gsap/Draggable'
import YcPoint from '../point'

export default {
  components: {
    YcPoint
  },
  props: {
    option: {},
    data: {
      default: () => {
        return {
          id: -1,
          x: 0,
          y: 0,
          text: '新节点',
          color: 'black', // 文字颜色
          bgColor: '#409eff', // 背景颜色
          hasHead: true, // 是否允许有输入节点
          hasTail: true, // 是否允许有输出节点
          isSelected: false // 是否被选中
        }
      }
    },
    info: { // 额外信息
      default: () => ({})
    }
  },
  data () {
    return {
      width: 0,
      isMoving: false,
      r: 5,
      head: { x: 0, y: 0 },
      tail: { x: 0, y: 0 },
      prev: null,
      next: null,
      textY: 0
    }
  },
  methods: {
    /**
     * 根据文本，重新计算并设置矩形宽度
     * @author Jiangzhiwei
     * @time   2021-11-27T23:50:29+0800
     */
    resetWidth () {
      this.$nextTick(() => {
        this.width = this.$refs.text.getComputedTextLength() + this.opt.padding * 2
      })
    },
    /**
     * 重置文本高度
     * @return {[type]} [description]
     * @author Jiangzhiwei
     * @time   2021-11-28T19:02:03+0800
     */
    resetTextY () {
      this.textY = 0
      this.$nextTick(() => {
        const bBox = this.$refs.text.getBBox()
        this.textY = (this.opt.height - bBox.height) / 2 - bBox.y
      })
    },
    setPrev (vm) {
      this.prev = vm
    },
    setNext (vm) {
      this.next = vm
    },
    enableDrag () {
      this.node.enable()
    },
    disableDrag () {
      this.node.disable()
    },
    updateHead (point) {
      this.head.x = point.x
      this.head.y = point.y
    },
    updateTail (point) {
      this.tail.x = point.x
      this.tail.y = point.y
    },
    isSelected () {
      return this.data.isSelected
    },
    /**
     * 改变选中状态，返回true表示状态改变
     * @param  {Boolean} isSelected 选中状态
     * @return {Boolean} 选中状态是否改变
     * @author Jiangzhiwei
     * @time   2021-11-27T20:16:20+0800
     */
    setSelected (isSelected = true) {
      if (this.data.isSelected !== isSelected) { // 状态不同
        this.data.isSelected = isSelected
        return true
      }
      return false
    },
    click (event) {
      event.stopPropagation()
    },
    mousedown (event) {
      event.stopPropagation()
    },
    mouseup (event) {
      event.stopPropagation()
    },
    remove () {
      this.$parent.removeNode(this.data.id)
    },
    getHead () {
      return this.head
    },
    getTail () {
      return this.tail
    },
    /**
     * 更新节点位置
     * @param    {Number} x 相对svg的x
     * @param    {Number} y 相对svg的y
     * @Author   Jiangzhiwei
     * @datetime 2021-10-22T11:30:06+0800
     */
    refreshPosition (x, y) {
      TweenMax.to(this.$refs.node, 0, { x, y })
      // console.log(this.isMoving)
      // console.log('move')
    },
    refreshData () {
      const matrix = this.$refs.node.transform.baseVal[0].matrix
      this.data.x = matrix.e
      this.data.y = matrix.f
    },
    /**
     * 根据x值更新相应的小圆点位置。如果不是移动状态，则更新节点的位置
     * @author Jiangzhiwei
     * @time   2021-11-27T23:48:39+0800
     */
    updateOnX () {
      const val = this.data.x
      this.head.x = val
      this.tail.x = val + this.width
      if (!this.isMoving) {
        this.refreshPosition(this.data.x, this.data.y)
      }
    },
    /**
     * 根据y值更新相应的小圆点位置。如果不是移动状态，则更新节点的位置
     * @author Jiangzhiwei
     * @time   2021-11-27T23:49:55+0800
     */
    updateOnY () {
      const val = this.data.y
      this.head.y = val + this.halfHeight
      this.tail.y = val + this.halfHeight
      if (!this.isMoving) {
        this.refreshPosition(this.data.x, this.data.y)
      }
    },
    /**
     * 移动其他选中的节点
     * @param  {Number} dx 变化的x位置
     * @param  {Number} dy 变化的y位置
     * @author Jiangzhiwei
     * @time   2021-11-27T23:47:08+0800
     */
    moveOtherSelectedNodes (dx, dy) {
      const nodeComps = this.$parent.getSelectedNodes(this)
      nodeComps.forEach(item => {
        item.data.x += dx
        item.data.y += dy
      })
    },
    init () {
      const stage = this.$parent
      this.box = stage.$el
      this.node = Draggable.create(this.$refs.node, {
        type: this.opt.moveType,
        edgeResistance: this.opt.edgeResistance,
        cursor: this.opt.cursor,
        activeCursor: this.opt.cursor,
        bounds: this.box,
        inertia: true,
        onPress: event => {
          // console.log('onPress')
          event.stopPropagation()
          if (this.data.isSelected) { // 如果已经被选中，则标记
            this.pressed = true // 标记选中时按下
            this.pressShift = event.shiftKey // 标记是否按下shift
          } else { // 未被选中
            const hasChanged = this.$parent.singleSelect(this, event.shiftKey)
            if (hasChanged && this.$parent.opt.recordSelected) { // 如果状态改变 且 需要记录
              this.$parent.addHistory()
            }
          }
          this.isMoving = true
          this.startX = this.data.x
          this.startY = this.data.y
        },
        onDrag: event => {
          this.refreshData()
          this.moveOtherSelectedNodes(this.data.x - this.startX, this.data.y - this.startY)
          this.startX = this.data.x
          this.startY = this.data.y
        },
        onDragEnd: event => {
          this.refreshData()
          this.moveOtherSelectedNodes(this.data.x - this.startX, this.data.y - this.startY)
          this.$parent.addHistory()
          // console.log(matrix.e, matrix.f)
          // console.log('onDragEnd')
          // console.log(this.head)
        },
        onClick: event => {
          // console.log('onClick')
          event.stopPropagation()
          // 进入onClick，表明未拖动
          if (this.pressed) { // 选中时按下
            if (this.pressShift) { // 按下了shift键
              const hasChanged = this.setSelected(false) // 取消选中
              if (hasChanged && this.$parent.opt.recordSelected) { // 如果状态改变 且 需要记录
                this.$parent.addHistory()
              }
            } else { // 未按下shift键
              const hasChanged = this.$parent.singleSelect(this) // 单选
              if (hasChanged && this.$parent.opt.recordSelected) { // 如果状态改变 且 需要记录
                this.$parent.addHistory()
              }
            }
          }
          this.pressed = false // 标记未按下
        },
        onRelease: event => {
          // console.log('onRelease')
          event.stopPropagation()
          this.isMoving = false
        }
        // onMove: event => {
        //   console.log('onMove')
        // },
        // onLockAxis: event => {
        //   console.log('onLockAxis')
        // }
      })[0]
      // this.data.text = '111'
      this.refreshPosition(this.data.x, this.data.y)
      this.resetWidth()
      this.resetTextY()
      this.$nextTick(() => {
        this.updateOnX()
        this.updateOnY()
      })
    }
  },
  computed: {
    opt () {
      const option = {
        fontSize: 16, // 字体大小
        borderWidth: 1, // 边框大小
        borderRadius: 3, // 边框圆角大小
        borderColor: '#5F4C0B', // 边框颜色
        selectedColor: '#F5DA81', // 选中后的边框颜色
        moveType: 'x,y', // 移动方式
        edgeResistance: 0.65, // 移出区域时的阻力
        cursor: 'default', // 鼠标拖动时的光标样式
        padding: 20, // 水平内间距
        height: 30, // 节点高度
        point: {}
      }
      return Object.assign(option, this.option)
    },
    strokeColor () {
      return this.data.isSelected ? this.opt.selectedColor : this.opt.borderColor
    },
    strokeWidth () {
      // return 1
      return this.data.isSelected ? this.opt.borderWidth + 1 : this.opt.borderWidth
    },
    halfHeight () {
      // console.log('halfHeight')
      return this.opt.height / 2
    },
    type () {
      return 'node'
    }
  },
  watch: {
    'data.x' (val) {
      this.updateOnX()
    },
    'data.y' (val) {
      this.updateOnY()
    },
    'data.text' () {
      this.resetWidth()
    },
    'opt.padding' (val) {
      this.resetWidth()
    },
    'opt.height' () {
      this.resetTextY()
    },
    'opt.fontSize' () {
      this.resetWidth()
      this.resetTextY()
    }
  },
  mounted () {
    this.init()
    // setTimeout(() => {
    //   this.data.text = '我'
    // }, 1000)
    // setTimeout(() => {
    //   this.data.text = '是一'
    // }, 2000)
    // setTimeout(() => {
    //   this.data.text = '只小红'
    // }, 3000)
    // setTimeout(() => {
    //   this.data.text = '帽'
    // }, 4000)
  }
}
</script>
